package name.zeno.qrcode.library.hui

import android.graphics.Bitmap
import android.support.annotation.IntRange
import com.google.zxing.EncodeHintType
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import com.google.zxing.qrcode.encoder.Encoder
import com.google.zxing.qrcode.encoder.QRCode


/**
 * @author 陈治谋 (513500085@qq.com)
 * @since 2017/11/30
 */
object QrCodeUtil {

  private const val QUIET_ZONE_SIZE = 4

  /**
   * # 对 zxing 的 QRCodeWriter 进行扩展, 解决白边过多的问题
   * @see com.google.zxing.qrcode.QRCodeWriter.encode
   */
  fun encode(qrCodeConfig: QrCodeOptions): BitMatrixEx {
    val errorCorrectionLevel =
        qrCodeConfig.hints[EncodeHintType.ERROR_CORRECTION] as? ErrorCorrectionLevel ?: ErrorCorrectionLevel.M

    var quietZone = qrCodeConfig.hints.get(EncodeHintType.ERROR_CORRECTION) as? Int ?: 1
    if (quietZone > QUIET_ZONE_SIZE) {
      quietZone = QUIET_ZONE_SIZE
    } else if (quietZone < 0) {
      quietZone = 0
    }

    val code = Encoder.encode(qrCodeConfig.msg, errorCorrectionLevel, qrCodeConfig.hints)

    return renderResult(code, qrCodeConfig.w, qrCodeConfig.h, quietZone)
  }


  fun toBitmap(options: QrCodeOptions): Bitmap = toBitmap(options, encode(options))

  /**
   * 根据二维码配置 & 二维码矩阵生成二维码图片
   *
   * @param qrCodeConfig
   * @param bitMatrix
   * @return
   * @throws IOException
   */
  fun toBitmap(qrCodeConfig: QrCodeOptions, bitMatrix: BitMatrixEx): Bitmap {
    val qrCodeWidth = bitMatrix.width
    val qrCodeHeight = bitMatrix.height

    // 1. 二维码基础绘制
    var qrCode = QRCodeDrawer.drawQrInfo(qrCodeConfig, bitMatrix)


    // 若二维码的实际宽高和预期的宽高不一致, 则缩放
    val realQrCodeWidth = qrCodeConfig.w
    val realQrCodeHeight = qrCodeConfig.h
    if (qrCodeWidth != realQrCodeWidth || qrCodeHeight != realQrCodeHeight) {
      val tmp = Bitmap.createBitmap(realQrCodeWidth, realQrCodeHeight, Bitmap.Config.ARGB_8888)
      //todo 缩放代码实现
//      tmp.getGraphics().drawImage(
//          qrCode.getScaledInstance(realQrCodeWidth, realQrCodeHeight,
//              Image.SCALE_SMOOTH), 0, 0, null)
      qrCode = tmp
    }


    /**
     * 说明
     * 在覆盖模式下，先设置二维码的透明度，然后绘制在背景图的正中央，最后绘制logo，这样保证logo不会透明，显示清晰
     * 在填充模式下，先绘制logo，然后绘制在背景的指定位置上；若先绘制背景，再绘制logo，则logo大小偏移量的计算会有问题
     */
    var logoDrawed = false
    // 绘制背景图
    // todo 重写
    if (qrCodeConfig.bgImgOptions != null) {
      if (qrCodeConfig.bgImgOptions?.bgImgStyle == BgImgOptions.FILL && qrCodeConfig.logo != null) {
        // 此种模式，先绘制logo
        qrCode = QRCodeDrawer.drawLogo(qrCode, qrCodeConfig)
        logoDrawed = true
      }

      qrCode = QRCodeDrawer.drawBackground(qrCode, qrCodeConfig.bgImgOptions!!)
    }
//
//
    // 插入logo
    if (qrCodeConfig.logo != null && logoDrawed.not()) {
      qrCode = QRCodeDrawer.drawLogo(qrCode, qrCodeConfig)
    }

    return qrCode
  }


  /**
   * 对 zxing 的 QRCodeWriter 进行扩展, 解决白边过多的问题
   *
   *
   * 源码参考 [com.google.zxing.qrcode.QRCodeWriter.renderResult]
   *
   * @param code
   * @param width
   * @param height
   * @param quietZone 取值 [0, 4]
   * @return
   */
  private fun renderResult(code: QRCode, width: Int, height: Int, @IntRange(from = 0, to = 4) quietZone: Int): BitMatrixEx {
    var width = width
    var height = height
    val input = code.matrix

    // xxx 二维码宽高相等, 即 qrWidth == qrHeight
    val inputWidth = input.width
    val inputHeight = input.height
    val qrWidth = inputWidth + quietZone * 2
    val qrHeight = inputHeight + quietZone * 2


    // 白边过多时, 缩放
    val minSize = Math.min(width, height)
    val scale = calculateScale(qrWidth, minSize)
    if (scale > 0) {
      print("qrCode scale enable! scale: $scale, qrSize:$qrWidth, expectSize:$width x $height")

      val padding: Int
      val tmpValue: Int
      // 计算边框留白
      padding = (minSize - qrWidth * scale) / QUIET_ZONE_SIZE * quietZone
      tmpValue = qrWidth * scale + padding
      when {
        width == height -> {
          width = tmpValue
          height = tmpValue
        }
        width > height -> {
          width = tmpValue * width / height
          height = tmpValue
        }
        else -> {
          height = tmpValue * height / width
          width = tmpValue
        }
      }
    }

    val outputWidth = Math.max(width, qrWidth)
    val outputHeight = Math.max(height, qrHeight)

    val multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight)
    val leftPadding = (outputWidth - inputWidth * multiple) / 2
    val topPadding = (outputHeight - inputHeight * multiple) / 2


    val res = BitMatrixEx(input)
    res.byteMatrix = input
    res.leftPadding = leftPadding
    res.topPadding = topPadding
    res.multiple = multiple

    res.width = outputWidth
    res.height = outputHeight
    return res
  }

  /**
   * 如果留白超过15% , 则需要缩放
   * (15% 可以根据实际需要进行修改)
   *
   * @param qrCodeSize 二维码大小
   * @param expectSize 期望输出大小
   * @return 返回缩放比例, <= 0 则表示不缩放, 否则指定缩放参数
   */
  private fun calculateScale(qrCodeSize: Int, expectSize: Int): Int {
    if (qrCodeSize >= expectSize) {
      return 0
    }

    val scale = expectSize / qrCodeSize
    val abs = expectSize - scale * qrCodeSize
    return if (abs < expectSize * 0.15) {
      0
    } else scale

  }

}